﻿
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "log.h"
#include "toolfun.h"
#include "haskelltool.h"
#include "barcode.h"

#include <algorithm>
#include <vector>
#include <functional>
#include <iostream>
#include <iomanip>
#include <tuple>
//typedef std::function< int(share_zone_t&, share_queue_t&, share_grid_t &)> GridFunType;
#define     Min(a,b)    (a) < (b)? (a) : (b)
//typedef std::function< int(int, int)> zipFunT;
void logInfoP(const char *fmt, ...);
template <typename T>
static  std::vector<T> zipWith(std::function< T(T, T)> fun,  std::vector<T>& list1, std::vector<T> &list2)
{
    int size = std::min(list1.size(), list2.size());
    std::vector<T> resultList;
    for(int i = 0; i < size; i++)
    {
        T result = fun(list1[i], list2[i]);
        resultList.push_back(result);
       // logInfo("%f+%f=%f", list1[i], list2[i], result);
    }
    return resultList;
}
template <typename T>
static T add(T x1, T x2)
{
    return x1 + x2;
}

template <typename T>
static T diff(T x1, T x2)
{
    return x1 - x2;
}
template <typename T>
void printFun(T x);


template <typename T>
void printVectorP( std::vector<T> list)
{
    std::cout<<"[";
    for(int i = 0; i < list.size(); i++)
    {
        printFun(list[i]);
        if (i != list.size() - 1)
        {
            std::cout<<",";
        }
    }
    std::cout<<"]";
}

template <typename T>
void printFun(T x)
{
  std::cout<<x;
}

template <>
void printFun(std::string x)
{
  std::cout<<"\""<<x<<"\"";
}

template <typename T1, typename T2>
void printFun(std::tuple<T1, T2> x)
{
    auto x1 = std::get<0>(x);
    auto x2 = std::get<1>(x);
    std::cout<<"("<<x1<<","<<x2<<")";
}

template <typename T>
void printFun( std::vector<T> list)
{
    std::cout<<"[";
    for(int i = 0; i < list.size(); i++)
    {
        printFun(list[i]);
        if (i != list.size() - 1)
        {
            std::cout<<",";
        }
    }
    std::cout<<"]";
}

template <>
void printFun( std::vector<char> list)
{
    std::cout<<"\"";
    for(int i = 0; i < list.size(); i++)
    {
        std::cout<<list[i];
        if (i != list.size() - 1)
        {
            std::cout<<"";
        }
    }
    std::cout<<"\"";
}

template <typename T>
 void printVector( std::vector<T> list, const char *prestr)
{
    std::cout <<prestr<<" ";
   // printVectorP(list);
    printFun(list);
    std::cout <<"\n";
}

template <typename T>
void print(const char *prestr, T x )
{
    std::cout <<prestr<<" ";;
    printFun(x);
    std::cout <<"\n";
}

template <typename T>
 void print(const char *prestr, std::vector<T> list)
{
    std::cout <<prestr;
    printFun(list);
    std::cout <<"\n";
}
template <typename T>
int length(T x)
{
    return x.size();
}

template <typename T, typename T2>
static std::vector<T2>
map( std::function< T2(T)>   fun,      std::vector<T> list)
{
    std::vector<T2> listNew;
    for (int i = 0; i < list.size(); i++)
    {
        T x1 = list[i];
        T2 x2 = fun(x1);
        listNew.push_back(x2);

    }
    return listNew;
}

template <typename T, typename T2>
static std::vector<T2>
mapr(std::vector<T> list, std::function< T2(T)>   fun)
{
    std::vector<T2> listNew;
    for (int i = 0; i < list.size(); i++)
    {
        T x1 = list[i];
        T2 x2 = fun(x1);
        listNew.push_back(x2);

    }
    return listNew;
}

std::vector<char> strToCharVector(std::string list)
{
    std::vector<char>listNew;
    for (int i = 0; i < list.size(); i++)
    {
        listNew.push_back(list[i]);
    }
    return listNew;
}

std::vector<std::vector<char> > strToVectorList(std::vector<std::string>& list)
{
   std::vector<std::vector<char> >listNew;
    for (int i = 0; i < list.size(); i++)
    {
        listNew.push_back( strToCharVector(list[i]));
    }
    return listNew;
}

char complement1 (char c)
{
    return c == '1' ? '0' : '1';
}

std::vector<char>  complement2(std::vector<char> list)
{
    return map<char, char>(complement1, list);
}
template <typename T>
T
reverse0(T list)
{
    T list2 = list;
    std::reverse(list2.begin(), list2.end());
    return list2;
}

template <typename T>
std::vector<T>
reverse(std::vector<T> list)
{
    std::vector<T> list2 = list;
    std::reverse(list2.begin(), list2.end());
    return list2;
}


template <typename T>
T
sum(std::vector<T> list)
{
    return std::accumulate(list.begin(), list.end(), 0);
}

std::vector<char>
reverse1(std::vector<char> list)
{
    std::vector<char> list2 = list;
    std::reverse(list2.begin(), list2.end());
    return list2;
}
template <typename T>
std::vector< std::vector<T> >
group(std::vector<T> list)
{
    std::vector< std::vector<T> > lists;
    std::vector<T> listTemp;
    listTemp.clear();
    listTemp.push_back(list[0]);
    for (int i = 1; i < list.size(); i++)
    {
        if (list[i] == list[i - 1])
        {
            listTemp.push_back(list[i]);
            continue;
        }
        else
        {
            lists.push_back(listTemp);
            listTemp.clear();
            listTemp.push_back(list[i]);
        }
    }
    lists.push_back(listTemp);
    return lists;
}
static void test1()
{
    std::vector<int> x1 {1,2,3,4};
    std::vector<int> x2{100,1000,100,1000};
    printVector<int>(x1, "x1");
    printVector<int>(x2, "x2");

    std::vector<int> list1 = zipWith<int>(add<int>,x1,x2);
    printVector<int>(list1, "result add");

    std::vector<int> list2 = zipWith<int>(diff<int>,x1,x2);
    printVector<int>(list2, "result diff");

    std::vector<double> y1 {1.1,2.1,3.1,4.1};
    std::vector<double> y2{200.5,2000.6,200.7,2000.8};

    printVector<double>(y1, "y1");
    printVector<double>(y2, "y2");

    std::vector<double> list11 = zipWith<double>(add<double>,y1,y2);
    printVector<double>(list11, "result add");

    std::vector<double> list12 = zipWith<double>(diff<double>,y1,y2);
    printVector<double>(list12, "result diff");

    std::vector<int> list4 = map<int, int>( [](int x){return x +5;}, list1);
    printVector(list4, "resultVector");
}

std::vector<int>
toRunLength(std::vector<char> cs1)
{
    return map< std::vector<char>, int >(
                  length<std::vector<char> >  ,
                     group(cs1));
}
template <typename T>
int
distance1(std::vector<T> xs1, std::vector<T> xs2)
{
    std::vector<T> ys1 = zipWith<T>(diff<T> ,xs1, xs2);
    std::vector<int> ys2 = map<T, int>(abs,ys1);
    int sum1 = sum<int>(ys2);
    return sum1;
}


int
distanceStr1(std::vector<char> xs1, std::vector<char> xs2)
{
    std::vector<int> ys1 = toRunLength(xs1);
    std::vector<int> ys2 = toRunLength(xs2);
    int sum1 = distance1<int>(ys1, ys2);
    int sum2 = (int)sum1;
    return sum2;
}

std::vector< std::tuple<int, int> >
zip(std::vector<int> xs1, std::vector<int> xs2)
{
    std::vector< std::tuple<int, int> > ys;
    int size = std::min( xs1.size(), xs2.size());
    for (int i = 0; i < size; i++)
    {
        std::tuple<int, int> ysItem{xs1[i], xs2[i]};
        ys.push_back(ysItem);

    }
    return ys;

}

template <typename T>
int comparefun(T t1, T t2)
{
    return t1 < t2;
}

template <typename T1, typename T2>
int comparefun(std::tuple<T1, T2>  t1, std::tuple<T1, T2>  t2)
{

    auto x1 = std::get<0>(t1);
    auto x2 = std::get<0>(t2);
    if (x1 == x2)
    {
       // logInfo("%d=%d, type", x1, x2);
        auto x11 = std::get<1>(t1);
        auto x12 = std::get<1>(t2);
        return x11 < x12;
    }
    else
    {
        return x1 < x2;
    }
}


template <typename T>
std::vector< T >
sort(std::vector<T> list1)
{
    std::vector<T> list2 = list1;
    std::sort(list2.begin(), list2.end(), comparefun<T>);
    return list2;
}

template <typename T>
std::vector< T >
take(int n, std::vector<T> list1)
{
    std::vector<T> list2(list1.begin(), list1.begin() + n);
    return list2;
}


std::vector< std::tuple<int, int> >
bestScores(std::vector< std::vector<char> >& srl, std::vector<char>& ps)
{
    auto ds = mapr<std::vector<char>, int>( srl,
        [&ps](std::vector<char> srl1){ return distanceStr1(srl1, ps);});
    //print("ds=", ds);
    std::vector<int> digits{0,1,2,3,4,5,6,7,8,9};
   // auto scores = zip(ds, digits);
  //  auto scores2 = sort(scores);
  //  auto scores3 = take(3, scores2);
    auto scores3 = take(3, sort< std::tuple<int, int> >(zip(ds, digits)));
    return scores3;
}

class ParityType{
public:
 int type;
 std::tuple<int, int> value;
 ParityType(int type, std::tuple<int, int> value)
 {
     this->type = type;
     this->value = value;
 }
};

std::tuple<int, int> fromParity(ParityType t)
{
    return t.value;
}

ParityType parityMap(ParityType t,std::function<std::tuple<int, int>(std::tuple<int, int>)> fun)
{
    return ParityType(t.type, fun(t.value));
}

ParityType NoneParity(std::tuple<int, int> value)
{
    return ParityType(0, value);
}
ParityType OddParity(std::tuple<int, int> value)
{
    return ParityType(1, value);
}
ParityType EvenParity(std::tuple<int, int> value)
{
   return ParityType(2, value);
}

template <>
void printFun(ParityType t)
{
    if (0 == t.type)
    {
        std::cout<<"None ";
    }
    else if (1 == t.type)
    {
        std::cout<<"Odd  ";
    }
    else
    {
        std::cout<<"Even ";
    }
    printFun(t.value);
}

template <>
int comparefun(ParityType  t1, ParityType  t2)
{
    return comparefun(t1.value, t2.value);
}

std::vector<std::string> leftOddList0
    {"0001101", "0011001", "0010011", "0111101", "0100011",
    "0110001", "0101111", "0111011", "0110111", "0001011"};

auto leftOddList =
        map< std::string, std::vector<char> >(strToCharVector, leftOddList0);
auto rightList = mapr<std::vector<char>, std::vector<char> >( leftOddList,
        [](std::vector<char> list) {return map<char, char>(complement1, list);});
auto leftEvenList = mapr<std::vector<char>, std::vector<char> >( rightList,
            reverse0< std::vector<char> >);

std::vector<std::string> parityList0
    {"111111", "110100", "110010", "110001", "101100",
     "100110", "100011", "101010", "101001", "100101"};
auto parityList =
        map< std::string, std::vector<char> >(strToCharVector, parityList0);

std::vector<ParityType>
bestRight(std::vector<char> ps)
{
    auto list1 =  bestScores(rightList, ps);
    auto list2 = mapr< std::tuple<int, int>   , ParityType>(list1, NoneParity);
    //print("list2=", list2);
    return list2;
}

std::vector<ParityType>
bestLeft(std::vector<char> ps)
{
    auto list2 = map< std::tuple<int, int>   , ParityType>( OddParity,
            bestScores(leftOddList, ps));
    auto list4 = map< std::tuple<int, int>   , ParityType>( EvenParity,
            bestScores(leftEvenList, ps));

    std::vector<ParityType> list5;
    list5.insert(list5.end(), list2.begin(), list2.end());
    list5.insert(list5.end(), list4.begin(), list4.end());

    return sort<ParityType>(list5);
}
std::vector< std::vector<char> >
chunksOf (int size1, std::vector<char> list)
{
    std::vector< std::vector<char> > listAll;
    std::vector<char> listTemp;
    for (int i = 0; i < list.size(); i++)
    {
        listTemp.push_back(list[i]);
        if (listTemp.size() >= size1)
        {
            listAll.push_back(listTemp);
            listTemp.clear();
        }
    }
    if (listTemp.size() > 0)
    {
        listAll.push_back(listTemp);
        listTemp.clear();
    }
    return listAll;
}

int pairtySnd( ParityType t)
{
    return std::get<1>(t.value);
}
void
candidateDigits(std::string rle)
{
    std::vector<char> rlev = strToCharVector(rle);
    std::vector<char> left1( rlev.begin() + 3, rlev.begin() + 45);
    std::vector<char> right1( rlev.begin() + 50, rlev.begin() + 92);
    auto left = chunksOf(7, left1);
    auto right = chunksOf(7, right1);
    auto left2 = map<std::vector<char>, std::vector<ParityType> >(bestLeft, left);
    print("left2=", left2);
    auto right2 = map<std::vector<char>, std::vector<ParityType> >(bestRight, right);
    print("right2=", right2);

    decltype(left2) match;
    match.insert(match.end(), left2.begin(), left2.end());
    match.insert(match.end(), right2.begin(), right2.end());

    auto result = mapr<std::vector<ParityType>,  std::vector<int> >(match, [](std::vector<ParityType> list){
        return mapr<ParityType, int >(list, pairtySnd)    ;}

    );

    print("result=", result);
}
static void test2()
{
    //distance,
    std::vector<int> xs1{2,2,2,2};
    std::vector<int> xs2{1,2,5,2};
    std::vector<int> ys1 = zipWith<int>(diff<int> ,xs1, xs2);
    printVector(ys1, "ys1");
    std::vector<int> ys2 = map<int, int>(abs,ys1);
    printVector(ys2, "ys2");
    int sum1 = sum<int>(ys2);
    logInfo("sum1=%d", sum1);

    std::vector<char> cs1 {'1','1', '3', '3', '3', '7', '3'};
    std::vector<char> cs3 {'1','1', '3', '3', '3', '7', '3'};
    std::vector<char> cs4 {'1','1', '3', '3', '3', '7', '3', '3', '3'};
    std::vector<char> cs5 {'1','1', '3', '3', '3', '7', '3'};
    printVector(cs1, "cs1");
    std::vector< std::vector<char> > cs2 = group(cs1);
    logInfo("cs2 size=%d", cs2.size());
    printVector(cs2, "cs2");

    //toIntList group
    std::vector<int> is1 {1,1,2,2,2,3,4,4,5};
    printVector(is1, "is1");
    std::vector< std::vector<int> > is2 = group(is1);
    logInfo("is2 size=%d", is2.size());
    printVector(is2, "is2");
    logInfo("dddddddddd");
    std::vector< std::vector< std::vector<int> > > is3;
    is3.push_back(is2);
    is3.push_back(is2);
    printVector(is3, "is3");
    double x=6;
    print("x=", x);
    print("is3=", is3);

    //group(is1);
    auto as1 = map< std::vector<char>, int >(
              length<std::vector<char> >  ,
                 group(cs1));
    auto as2 = toRunLength(cs1);
    print("as1=", as1);
    print("as1=", as2);

    //distanceStr

    auto ds1 = distanceStr1(cs3, cs4);
    print("ds1=", ds1);
    //bestScores

    auto digit1 =  strToCharVector("0011001");
    auto result1 = bestScores(leftOddList, digit1);
    print("bestScores:result1=", result1);
    auto leftResult = bestLeft(digit1);
    print("leftResult=", leftResult);

    auto digit2 =  strToCharVector("1100110");
    auto rightResult = bestRight(digit2);
    print("rightResult= ", rightResult);
    //auto result2 = bestRight(digit2);

    auto lists = chunksOf(7, strToCharVector("0011001211111131111114111"));
    print("lists=", lists);

    candidateDigits(
        "10100110010011001011001100110010110011011001101010110011011001101100110110011011001101010000101"
                    );
}

int main_barcode3()
{
    logInfo("***************BarCode*************");
    test2();
   return 0;





    print("leftOddList0", leftOddList0);
    print("leftOddList ", leftOddList);
    print("rightList   ", rightList);
    print("leftEvenList", leftEvenList);
    print("parityList  ", parityList);







    return 0;
}
